package psn.kiko.service.business.system;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import psn.kiko.annotation.api.APIStatus;
import psn.kiko.annotation.api.StableID;
import psn.kiko.constant.APIStatusConstant;
import psn.kiko.constant.DBConstant;
import psn.kiko.constant.MessageConstant;
import psn.kiko.excepion.BaseBusinessException;
import psn.kiko.excepion.FileOperationException;
import psn.kiko.mapper.business.system.InterfaceMapper;
import psn.kiko.pojo.PageResult;
import psn.kiko.pojo.dto.InterfaceDTO;
import psn.kiko.pojo.entity.system.InterfaceEntity;
import psn.kiko.pojo.vo.DelDetailVO;
import psn.kiko.pojo.vo.InterfacePageVO;
import psn.kiko.pojo.vo.LabelValue;
import psn.kiko.pojo.vo.PureInterfaceVO;
import psn.kiko.service.business.associated.InterfaceRoleService;
import psn.kiko.service.business.associated.OperationInterfaceService;

import javax.annotation.Resource;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 接口管理服务
 */
@Service
public class InterfaceService{
	@Autowired
	InterfaceMapper interfaceMapper;
	@Autowired
	InterfaceRoleService interfaceRoleService;
	@Resource
	OperationInterfaceService operationInterfaceService;
	
	/**
	 * 根据主键集合批量删除接口信息，以及中间表信息（仅删除与已废弃接口关联的）
	 * <p style="color:red;">添加事务控制</p>
	 */
	public ArrayList<DelDetailVO> batchDeleteApiInfoByIds(List<Integer> interfaceIds){
		//查询已废弃的接口，删除与已废弃接口管理的 操作-接口、接口-角色 记录
		List<PureInterfaceVO> deprecatedInterfaces = interfaceMapper.selectDeprecatedInterfaces(interfaceIds);
		List<Integer> deprecatedStableIds = deprecatedInterfaces.stream()
				.map(api->api.getStableId())
				.collect(Collectors.toList());
		Integer c1 = 0;
		Integer c2 = 0;
		if(deprecatedStableIds.size() > 0){
			//操作-接口（已废弃接口）
			c1 = operationInterfaceService.deleteBatchByApiStableIds(deprecatedStableIds);
			//接口-角色（已废弃接口）
			c2 = interfaceRoleService.batchDeleteByInterfaceIds(deprecatedStableIds);
		}
		//删除自身数据
		Integer c = interfaceMapper.deleteByIds(interfaceIds);
		
		ArrayList<DelDetailVO> delDetailVOS = new ArrayList<>();
		delDetailVOS.add(new DelDetailVO("删除的接口数量",c));
		delDetailVOS.add(new DelDetailVO("删除的与已废弃的接口关联的操作数量",c1));
		delDetailVOS.add(new DelDetailVO("删除的与已废弃的接口关联的角色数量",c2));
		return delDetailVOS;
	}
	
	/**
	 * 列举接口所属分组选项
	 */
	public Set<String> listApiGroups(){
		return interfaceMapper.listApiGroups();
	}
	
	/**
	 * 列举接口所属类选项
	 */
	public Set<String> listApiClassNames(){
		return interfaceMapper.listApiClassNames();
	}
	
	/**
	 * 列举接口请求方式选项
	 */
	public Set<String> listApiRequestMethods(){
		return interfaceMapper.lisApiRequestMethods();
	}
	
	/**
	 * 列举接口状态选项
	 */
	public List<LabelValue> listApiStatuses(){
		return DBConstant.getStatusesWithLabel();
	}
	
	/**
	 * 分页获取接口集合，携带查询条件
	 */
	public PageResult getInterfaceEntities(InterfaceDTO interfaceDTO,int pageNum,int pageSize){
		Page<Object> page = PageHelper.startPage(pageNum,pageSize);
		List<InterfacePageVO> interfaceVOS = interfaceMapper.selectList(interfaceDTO);
		PageResult pageResult = PageResult.builder()
				.data(interfaceVOS)
				.total(Integer.parseInt(String.valueOf(page.getTotal())))
				.build();
		return pageResult;
	}
	
	/**
	 * 以单个@Controller文件为解析单位，解析出接口信息，并保存到数据库
	 * <p style="color:red;">添加事务控制</p>
	 */
	@Transactional
	public Integer parseControllerFile(String controllerFile){
		Integer insertCount = 0;
		try{
			//解析指定controller文件
			List<InterfaceEntity> list = parseFile(controllerFile);
			//插入数据
			insertCount = interfaceMapper.insertList(list);
		}catch(Exception e){
			e.printStackTrace();
			//接口信息已全部或部分解析入库
			if(e instanceof DuplicateKeyException){
				throw new BaseBusinessException(MessageConstant.FILE_PARSE_FAIL_CAUSE_EXISTS);
			}
			//其它情况
			else{
				throw new FileOperationException(MessageConstant.FILE_PARSE_FAIL);
			}
		}
		return insertCount;
	}
	
	/**
	 * <p>解析@Controller文件中的接口信息，封装为集合</p>
	 */
	private List<InterfaceEntity> parseFile(String controllerFile) throws ClassNotFoundException{
		//若不是class字节码文件
		if(!controllerFile.endsWith(".class")){
			return null;
		}
		File file = new File(controllerFile);
		//若文件不存在
		if(file == null||!file.exists()){
			return null;
		}
		//文件绝对路径
		String absolutePath = file.getAbsolutePath();
		
		//解析的类名
		String className = absolutePath.split("classes\\\\")[1].replaceAll("\\\\",".");
		className = className.substring(0,className.lastIndexOf(".class"));
		//接口实体集合
		List<InterfaceEntity> interfaceEntities = new ArrayList<>();
		Class<?> clazz = Class.forName(className);
		RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
		//请求路径前半部分（标注于类的注解上）
		String[] prefixPaths = requestMapping.value();
		Api api = clazz.getAnnotation(Api.class);
		//接口标记
		String[] tags = api.tags();
		// 接口分组
		String apiGroup = file.getName();
		if(tags.length > 0){
			apiGroup = tags[0];
		}
		
		Method[] declaredMethods = clazz.getDeclaredMethods();
		for(Method method: declaredMethods){
			Annotation[] methodAnnotations = method.getAnnotations();
			String[] requestPaths = null;//接口请求路径数组
			String description = "";//接口描述
			String requestMethod = "";//接口请求方式
			int stableId = -1;//接口稳定ID
			short deprecated = APIStatusConstant.UN_DEPRECATED;//接口是否已废弃
			
			//方法注解处理
			for(Annotation methodAnnotation: methodAnnotations){
				//解析接口后半部分请求路径、请求方式以及稳定ID
				if(methodAnnotation instanceof RequestMapping){
					RequestMapping rm = (RequestMapping) methodAnnotation;
					RequestMethod[] methods = rm.method();
					requestMethod = methods.length > 0 ? methods[0].name() : "---";
					requestPaths = rm.value();
				}else if(methodAnnotation instanceof PostMapping){
					requestPaths = ((PostMapping) methodAnnotation).value();
					requestMethod = RequestMethod.POST.name();
				}else if(methodAnnotation instanceof GetMapping){
					requestPaths = ((GetMapping) methodAnnotation).value();
					requestMethod = RequestMethod.GET.name();
				}else if(methodAnnotation instanceof PutMapping){
					requestPaths = ((PutMapping) methodAnnotation).value();
					requestMethod = RequestMethod.PUT.name();
				}else if(methodAnnotation instanceof DeleteMapping){
					requestPaths = ((DeleteMapping) methodAnnotation).value();
					requestMethod = RequestMethod.DELETE.name();
				}else if(methodAnnotation instanceof PatchMapping){
					requestPaths = ((PatchMapping) methodAnnotation).value();
					requestMethod = RequestMethod.PATCH.name();
				}
				
				// 解析接口说明文字
				if(methodAnnotation instanceof ApiOperation){
					description = ((ApiOperation) methodAnnotation).value();
				}
				//解析稳定ID
				if(methodAnnotation instanceof StableID){
					stableId = ((StableID) methodAnnotation).value();
				}
				//解析接口是否已废弃
				if(methodAnnotation instanceof APIStatus){
					boolean apiDeprecated = ((APIStatus) methodAnnotation).deprecated();
					if(apiDeprecated){
						deprecated = APIStatusConstant.DEPRECATED;
					}
				}
			}
			//请求路径前缀(以斜线开始和结尾)
			String prefixRequestPath = "";
			if(prefixPaths.length > 0){
				//补结尾斜线
				prefixRequestPath = prefixPaths[0].endsWith("/") ?
						prefixPaths[0] :
						prefixPaths[0] + "/";
				//补开始斜线
				prefixRequestPath = prefixRequestPath.startsWith("/") ?
						prefixRequestPath :
						"/" + prefixRequestPath;
			}
			//请求路径后缀(去除开头斜线和结尾斜线)
			String suffixRequestPath = "";
			if(requestPaths != null&&requestPaths.length > 0){
				suffixRequestPath = requestPaths[0];
				//去除开始斜线
				suffixRequestPath = suffixRequestPath.startsWith("/") ?
						requestPaths[0].replaceFirst("/","") :
						suffixRequestPath;
				//去除结尾斜线
				suffixRequestPath = suffixRequestPath.endsWith("/") ?
						suffixRequestPath.substring(0,suffixRequestPath.lastIndexOf("/")) :
						suffixRequestPath;
			}
			
			if(prefixRequestPath != ""||suffixRequestPath != ""){
				// 完整路径类似 /xxx/yyy/zzz (两者都不为空字符串时)
				// 或者 /aa/bb/ (suffixRequestPath为空字符串时)
				// 或者 cc (prefixRequestPath为空字符串时)
				String fullPath = prefixRequestPath + suffixRequestPath;
				//构建实体
				InterfaceEntity interfaceEntity = InterfaceEntity.builder()
						.stableId(stableId)
						.deprecated(deprecated)
						.className(className)
						.description(description)
						.apiGroup(apiGroup)
						.requestPath(fullPath)
						.requestMethod(requestMethod)
						.build();
				//添加到集合
				interfaceEntities.add(interfaceEntity);
			}
		}
		
		return interfaceEntities;
	}
	
	/**
	 * 根据id批量启用接口
	 */
	public Integer batchEnableApisByIds(List<Integer> apiIds){
		return interfaceMapper.updateStatusByIds(apiIds,DBConstant.ENABLE);
	}
	
	/**
	 * 根据id批量禁用接口
	 */
	public Integer batchDisableApisByIds(List<Integer> apiIds){
		return interfaceMapper.updateStatusByIds(apiIds,DBConstant.DISABLE);
	}
	
	/**
	 * 根据id更新接口
	 */
	public Integer updateInterface(InterfaceDTO interfaceDTO){
		Short status = interfaceDTO.getEnabled();
		List<Short> allStatus = DBConstant.getStatuses();
		if(!allStatus.contains(status)){
			interfaceDTO.setEnabled(DBConstant.DISABLE);
		}
		InterfaceEntity interfaceEntity = new InterfaceEntity();
		BeanUtils.copyProperties(interfaceDTO,interfaceEntity);
		return interfaceMapper.updateById(interfaceEntity);
	}
	
	/**
	 * 获取指定请求路径和请求方式的接口
	 */
	public InterfaceEntity selectByPathAndMethod(String requestPath,String requestMethod){
		return interfaceMapper.selectByPathAndMethod(requestPath,requestMethod);
	}
	
	/**
	 * 根据主键获取接口
	 */
	public PureInterfaceVO getIntefaceById(Integer interfaceId){
		return interfaceMapper.selectById(interfaceId);
	}
}
